#include <stdio.h>
#include <unistd.h>
#include "Thread.h"
#include "Broker.h"
#include "Publisher.h"
#include "Subscriber.h"
#include "Messages.pb.h"
#include "Ping.h"
#include "Pong.h"

class Msg01SeqReader : public Subscriber::DataReaderTemplate<fsk::msg::Message01>
{

  private:
    unsigned K;
    unsigned val_;

    void handle(fsk::msg::Message01& msg)
    {
      printf("(%s:%d) handling msg 01 -- %d\n",__FILE__,__LINE__, msg.val01());
      val_=msg.val01();
      assert(K == val_);
      ++K;
    }

  public:
    Msg01SeqReader():K(0) { }
    unsigned val() { return val_; }
};

class Msg01Reader : public Subscriber::DataReaderTemplate<fsk::msg::Message01>
{

  private:
    unsigned val_;

    void handle(fsk::msg::Message01& msg)
    {
      printf("(%s:%d) handling msg 01 -- %d\n",__FILE__,__LINE__, msg.val01());
      val_=msg.val01();
    }

  public:
    Msg01Reader() { }
    unsigned val() { return val_; }
};


void test01()
{
  const int NumIterations = 10000;
  Publisher pub;
  fsk::msg::Message01 msg;
  Subscriber sub;
  Msg01SeqReader dr;
  sub.registerHandler(msg,dr);
  ::usleep(5000);

//sub.start();
  for(int i=0; i<NumIterations; ++i)
  {
    msg.set_val01(i);
    pub.publish(msg);
    ::usleep(500);
  }
  assert(dr.val() == NumIterations-1);
}

void test02()
{
  const int NumIterations = 10000;
  Publisher pub;
  fsk::msg::Message01 msg;
  Subscriber sub;
  Msg01SeqReader dr01;
  Msg01SeqReader dr02;
  sub.registerHandler(msg,dr01);
  sub.registerHandler(msg,dr02);
  ::usleep(5000);

//sub.start();
  for(int i=0; i<NumIterations; ++i)
  {
    msg.set_val01(i);
    pub.publish(msg);
    ::usleep(500);
  }
  assert(dr01.val() == NumIterations-1);
  assert(dr02.val() == NumIterations-1);
}

void test03()
{
  enum State { S1=1, S2, S3, S4};
  static const unsigned N=10000;
  Publisher pub;
  fsk::msg::Message01 msg;
  Subscriber sub;
  Msg01Reader dr01;
  Msg01Reader dr02;
//sub.start();

  for(int s=S1; s<=S4; ++s)
  {
    switch(s)
    {
      case S1:
        {
          sub.registerHandler(msg,dr01);
          sub.registerHandler(msg,dr02);
          ::usleep(1000);
          for(int i=(s-1)*N; i<s*N; ++i)
          {
            printf("(%s:%d) i(%d)\n",__FILE__,__LINE__,i);
            msg.set_val01(i);
            pub.publish(msg);
            ::usleep(2000);
            assert(dr01.val() == i);
            assert(dr02.val() == i);
          }
          sub.unregisterHandler(msg,dr01);
          sub.unregisterHandler(msg,dr02);
        }
        break;
      case S2:
        {
          sub.registerHandler(msg,dr01);
          ::usleep(1000);
          for(int i=(s-1)*N; i<s*N; ++i)
          {
            printf("(%s:%d) i(%d)\n",__FILE__,__LINE__,i);
            msg.set_val01(i);
            pub.publish(msg);
            ::usleep(2000);
            assert(dr01.val() == i);
            assert(dr02.val() == ((s-1)*N-1));
          }
          sub.unregisterHandler(msg,dr01);
        }
        break;
      case S3:
        {
          sub.registerHandler(msg,dr02);
          ::usleep(1000);
          for(int i=(s-1)*N; i<s*N; ++i)
          {
            printf("(%s:%d) i(%d)\n",__FILE__,__LINE__,i);
            msg.set_val01(i);
            pub.publish(msg);
            ::usleep(2500);
            assert(dr02.val() == i);
            assert(dr01.val() == ((s-1)*N-1));
          }
          sub.unregisterHandler(msg,dr02);
        }
        break;
      case S4:
        {
          for(int i=(s-1)*N; i<s*N; ++i)
          {
            printf("(%s:%d) i(%d)\n",__FILE__,__LINE__,i);
            msg.set_val01(i);
            pub.publish(msg);
            ::usleep(2000);
            assert(dr01.val() == ((s-2)*N-1));
            assert(dr02.val() == ((s-1)*N-1));
          }
        }
        break;
      default:
        {
          assert(0);
        }
    }
  }
}

void test04()
{
  Pong pong;
  Ping ping;
  const int Secs=30;
  printf("(%s:%d) running for %d seconds\n",__FILE__,__LINE__,Secs);
  ::usleep(Secs*1000000);
}

int main(int argc, char* argv[])
{
  printf("(%s:%d) main process initializing\n",__FILE__,__LINE__);
  Broker broker;
  broker.start();
  ::usleep(1000000);

  //test01();
  //test02();
  //test03();
  test04();

  printf("(%s:%d) main process terminating\n",__FILE__,__LINE__);
}
